﻿using System;
using System.Collections.Generic;
using System.Text;

//map to user information
using RoleCodeMap = System.Collections.Generic.Dictionary<string, UsrInfoCacheUnit>;
//map to user information
using SessionMap = System.Collections.Generic.Dictionary<int, int>;
//map to session identifer
using CodeMap = System.Collections.Generic.Dictionary<int, UsrInfoCacheUnit>;

using ValidCodeArray = System.Collections.Generic.Stack<int>;

public class UsrInfoCacheUnit
{
    #region property
    //identifier of user account
    private int m_UsrId;
    /// <summary>
    /// identifier of user account
    /// </summary>
    public int UsrId
    {
        get
        {
            return m_UsrId;
        }
        private set
        {
            m_UsrId = value;
        }
    }
    //role code
    private string m_roleCode;
    /// <summary>
    /// role code
    /// </summary>
    public string RoleCode
    {
        get
        {
            return m_roleCode;
        }
        private set
        {
            m_roleCode = value;
        }
    }
    //gateway passport
    private int m_gateCode;
    /// <summary>
    /// gateway passport
    /// </summary>
    public int GateCode
    {
        get
        {
            return m_gateCode;
        }
        private set
        {
            m_gateCode = value;
        }
    }
    //name of the user
    private string m_UsrName;
    /// <summary>
    /// name of the user
    /// </summary>
    public string UsrName
    {
        get
        {
            return m_UsrName;
        }
        private set
        {
            m_UsrName = value;
        }
    }
    //world identifier of role
    private int m_WorldId;
    /// <summary>
    /// world identifier of role
    /// </summary>
    public int WorldId
    {
        get
        {
            return m_WorldId;
        }
        private set
        {
            m_WorldId = value;
        }
    }
    //identifier of the session that this usr connected with
    private int m_SessionId;
    /// <summary>
    /// identifier of the session that this usr connected with
    /// </summary>
    public int SessionId
    {
        get
        {
            return m_SessionId;
        }
        private set
        {
            m_SessionId = value;
        }
    }
    #endregion

    #region constructor
    /// <summary>
    /// constructor
    /// </summary>
    public UsrInfoCacheUnit(int usr_id, int gate_code, string role_code, string usr_name, int world_id, int session_id)
    {
        m_gateCode  = gate_code;
        m_UsrId     = usr_id;
        m_roleCode  = role_code;
        m_UsrName   = usr_name;
        m_WorldId   = world_id;
        m_SessionId = session_id;
    }
    #endregion
}

public class UsrInfoCache
{
    #region private variable
    //user information map
    private RoleCodeMap m_mapRoleCode = new RoleCodeMap();
    //session map
    private SessionMap m_mapSession = new SessionMap();
    //code map
    private CodeMap m_mapCode = new CodeMap();
    //code left
    private ValidCodeArray m_arrayValid = new ValidCodeArray();
    //maxium code value
    private Int32 m_maxCode = 0;
    //lock object
    private Object m_lockObj = new Object();
    #endregion

    #region private call
    /// <summary>
    /// get a valid code
    /// </summary>
    /// <returns></returns>
    private int GetValidCode()
    {
        if (m_arrayValid.Count > 0)
            return m_arrayValid.Pop();
        else if (m_maxCode < Int32.MaxValue)
        {
            return m_maxCode++;
        }

        return GlobalDef.INVALID_GATE_CODE;
    }
    #endregion

    #region constructor
        /// <summary>
    /// constuctor
    /// </summary>
    /// <param name="max_code"></param>
    public UsrInfoCache(int max_code)
    {
        for (m_maxCode = 0; m_maxCode < max_code; ++m_maxCode)
        {
            m_arrayValid.Push(m_maxCode);
        }
    }
    #endregion

    #region external call
    /// <summary>
    /// add a user information
    /// </summary>
    /// <param name="usr_id"></param>
    /// <param name="role_id"></param>
    /// <param name="usr_name"></param>
    /// <param name="world_id"></param>
    /// <param name="session_id"></param>
    /// <returns></returns>
    public bool Add(int usr_id, string role_code, string usr_name, int world_id, int session_id, out int gate_code)
    {
        gate_code = GlobalDef.INVALID_GATE_CODE;
        lock (m_lockObj)
        {
            if (m_mapRoleCode.ContainsKey(role_code) == false)
            {
                gate_code = GetValidCode();
                UsrInfoCacheUnit unit = new UsrInfoCacheUnit(usr_id, gate_code, role_code, usr_name, world_id, session_id);
                m_mapRoleCode.Add(role_code, unit);
                m_mapCode.Add(gate_code, unit);
                m_mapSession.Add(session_id, gate_code);
            }

            return true;
        }

        return false;
    }
    /// <summary>
    /// get a user information by the gate code
    /// </summary>
    /// <param name="gate_code"></param>
    /// <returns></returns>
    public UsrInfoCacheUnit GetByGateCode(int gate_code)
    {
        lock (m_lockObj)
        {
            UsrInfoCacheUnit unit;
            if (m_mapCode.TryGetValue(gate_code, out unit))
                return unit;
        }

        return null;
    }

    /// <summary>
    /// remove a user information by the session identifier
    /// </summary>
    /// <param name="usr_id"></param>
    public void RemoveBySessionId(int session_id)
    {
        lock (m_lockObj)
        {
            if (m_mapSession.ContainsKey(session_id) == true)
            {
                UsrInfoCacheUnit unit = m_mapCode[m_mapSession[session_id]];
                m_mapSession.Remove(session_id);
                m_mapRoleCode.Remove(unit.RoleCode);
                m_mapCode.Remove(unit.GateCode);

                m_arrayValid.Push(unit.GateCode);
            }
        }
    }
    #endregion
}
